import * as vscode from 'vscode';
import { getAgentsManual } from './agentsManual';
import { MagicApiClient } from './magicApiClient';
import { MagicResourceType, MAGIC_RESOURCE_TYPES } from './types';
import { addFileError, clearFileErrors } from './diagnostics';

export interface MagicFileInfo {
    id: string;
    name: string;
    path: string;
    script: string;
    groupId: string;
    groupPath: string;
    type: MagicResourceType;
    createTime?: number;
    updateTime?: number;
    createBy?: string;
    updateBy?: string;
    method?: string;
    requestMapping?: string;
    description?: string;
    locked?: boolean;
    /** 完整 URL 路径（从资源树中预计算得到） */
    fullUrlPath?: string;
    // 类型专属字段：API
    params?: any[];
    headers?: Record<string, any> | any;
    contentType?: string;
    timeout?: number;
    // 类型专属字段：任务
    cron?: string;
    enabled?: boolean;
    executeOnStart?: boolean;
    // 其他服务端扩展字段（未识别的字段原样保留）
    extra?: Record<string, any>;
    /** 原始服务端返回对象（仅在 getFile 获取单个文件时可用） */
    raw?: any;
}

export interface MagicGroupInfo {
    id: string;
    name: string;
    path: string;
    parentId?: string;
    type: MagicResourceType;
    createTime?: number;
    updateTime?: number;
}

export class MagicFileSystemProvider implements vscode.FileSystemProvider {
    private _emitter = new vscode.EventEmitter<vscode.FileChangeEvent[]>();
    private _bufferedEvents: vscode.FileChangeEvent[] = [];
    private _fireSoonHandle?: NodeJS.Timer;

    readonly onDidChangeFile: vscode.Event<vscode.FileChangeEvent[]> = this._emitter.event;

    constructor(private client: MagicApiClient, private configDir?: vscode.Uri) {}

    private isAgentsUri(uri: vscode.Uri): boolean {
        const p = uri.path.replace(/^\/+/, '');
        return p === 'AGENTS.md';
    }

    private getAgentsFileUri(): vscode.Uri | undefined {
        if (!this.configDir) return undefined;
        return vscode.Uri.joinPath(this.configDir, 'AGENTS.md');
    }

    private getExtForType(type: MagicResourceType): string {
        return type === 'datasource' ? '.json' : '.ms';
    }

    private async ensureConfigDirExists(): Promise<void> {
        if (!this.configDir) return;
        try {
            await vscode.workspace.fs.stat(this.configDir);
        } catch {
            await vscode.workspace.fs.createDirectory(this.configDir);
        }
    }

    private generateAgentsContent(): string {
        return getAgentsManual();
    }

    private async readAgentsFile(): Promise<Uint8Array> {
        const agentsUri = this.getAgentsFileUri();
        if (!agentsUri) {
            const content = this.generateAgentsContent();
            return Buffer.from(content, 'utf8');
        }
        await this.ensureConfigDirExists();
        let exists = true;
        try {
            await vscode.workspace.fs.stat(agentsUri);
        } catch {
            exists = false;
        }
        if (!exists) {
            const content = this.generateAgentsContent();
            await vscode.workspace.fs.writeFile(agentsUri, Buffer.from(content, 'utf8'));
        }
        return vscode.workspace.fs.readFile(agentsUri);
    }

    private async writeAgentsFile(content: Uint8Array): Promise<void> {
        const agentsUri = this.getAgentsFileUri();
        if (!agentsUri) return;
        await this.ensureConfigDirExists();
        await vscode.workspace.fs.writeFile(agentsUri, content);
    }

    // 监听文件变化
    watch(uri: vscode.Uri, options: { recursive: boolean; excludes: string[]; }): vscode.Disposable {
        // 实现文件监听逻辑
        return new vscode.Disposable(() => {});
    }

    // 获取文件状态（统一资源目录结构）
    async stat(uri: vscode.Uri): Promise<vscode.FileStat> {
        if (this.isAgentsUri(uri)) {
            return { type: vscode.FileType.File, ctime: Date.now(), mtime: Date.now(), size: (await this.readAgentsFile()).length };
        }
        const p = this.parsePath(uri);
        if (p.isRoot) {
            return { type: vscode.FileType.Directory, ctime: Date.now(), mtime: Date.now(), size: 0 };
        }

        if (p.isFile) {
            const type = p.dir.split('/')[0] as MagicResourceType;
            const ext = this.getExtForType(type);
            const filePath = `${p.dir}/${p.fileName}${ext}`;
            let fid = this.client.getFileIdByPath(filePath);
            if (!fid) {
                // 填充缓存（按目录刷新）
                await this.client.getResourceFiles(p.dir);
                fid = this.client.getFileIdByPath(filePath);
            }
            if (!fid) {
                // 兜底：全树路径匹配解析
                fid = await this.client.resolveFileIdByPath(filePath);
            }
            if (!fid) throw vscode.FileSystemError.FileNotFound(uri);
            const fileInfo = await this.client.getFile(fid);
            if (!fileInfo) throw vscode.FileSystemError.FileNotFound(uri);
            // datasource 使用 JSON 视图，其余类型使用脚本内容
            const contentStr = type === 'datasource'
                ? this.serializeDatasourceContent(fileInfo)
                : (fileInfo.script || '');
            return {
                type: vscode.FileType.File,
                ctime: fileInfo.createTime || Date.now(),
                mtime: fileInfo.updateTime || Date.now(),
                size: Buffer.byteLength(contentStr, 'utf8')
            };
        }

        // 目录：仅由统一目录列表驱动
        const files = await this.client.getResourceFiles(p.dir);
        if (files) {
            return { type: vscode.FileType.Directory, ctime: Date.now(), mtime: Date.now(), size: 0 };
        }
        throw vscode.FileSystemError.FileNotFound(uri);
    }

    // 读取目录（统一资源目录结构）
    async readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
        const p = this.parsePath(uri);
        const entries: [string, vscode.FileType][] = [];

        const dirs = this.client ? await this.client.getResourceDirs() : [];
        if (p.isRoot) {
            // 展示顶层目录：加入服务返回的第一段，并始终包含类型根
            const topSet = new Set<string>();
            for (const t of MAGIC_RESOURCE_TYPES) topSet.add(t);
            for (const d of dirs) {
                const seg = d.split('/')[0];
                if (seg) topSet.add(seg);
            }
            for (const name of Array.from(topSet)) entries.push([name, vscode.FileType.Directory]);
            // 根目录追加 AGENTS.md
            entries.push(['AGENTS.md', vscode.FileType.File]);
            return entries;
        }

        // 子目录：展示 p.dir 的子目录与文件（无客户端时返回空）
        const childDirSet = new Set<string>();
        const prefix = p.dir + '/';
        for (const d of dirs) {
            if (d.startsWith(prefix)) {
                const rest = d.substring(prefix.length);
                const next = rest.split('/')[0];
                if (next) childDirSet.add(next);
            }
        }
        for (const name of Array.from(childDirSet)) entries.push([name, vscode.FileType.Directory]);

        let files = this.client ? await this.client.getResourceFiles(p.dir) : [];
        const type = p.dir.split('/')[0] as MagicResourceType;
        const ext = this.getExtForType(type);

        // Fallback: if no files in the directory, try fetching by type + groupId
        if ((!files || files.length === 0) && this.client) {
            await this.client.getResourceDirs();
            const groupId = this.client.getGroupIdByPath(p.dir) || null;
            const getFilesFn = (this.client as any).getFiles;
            if (typeof getFilesFn === 'function') {
                const fallbackFiles = await getFilesFn.call(this.client, type, groupId);
                if (fallbackFiles && fallbackFiles.length) {
                    files = fallbackFiles.map((f: any) => ({ id: f.id, name: f.name, type: f.type }));
                }
            }
        }

        for (const f of files) entries.push([`${f.name}${ext}`, vscode.FileType.File]);
        return entries;
    }

    // 创建目录
    async createDirectory(uri: vscode.Uri): Promise<void> {
        const p = this.parsePath(uri);
        if (p.isRoot) {
            throw vscode.FileSystemError.NoPermissions('Cannot create directory at root level');
        }
        // 目录创建依旧走分组接口（后端已有），通过父目录路径解析 parentId
        const parentUri = vscode.Uri.parse(uri.toString().substring(0, uri.toString().lastIndexOf('/')));
        const parent = this.parsePath(parentUri);
        const groupName = uri.path.split('/').pop()!;
        const type = parent.dir.split('/')[0] as MagicResourceType;
        const groupPathSub = parent.dir.split('/').slice(1).join('/');
        // 通过资源树填充缓存并解析 parentId
        await this.client.getResourceDirs();
        const parentId = this.client.getGroupIdByPath(`${type}/${groupPathSub}`) || null;

        await this.client.createGroup({ name: groupName, parentId, type });

        this._fireSoon({ type: vscode.FileChangeType.Created, uri });
    }

    // 读取文件
    async readFile(uri: vscode.Uri): Promise<Uint8Array> {
        if (this.isAgentsUri(uri)) {
            return this.readAgentsFile();
        }
        const p = this.parsePath(uri);
        const type = p.dir.split('/')[0] as MagicResourceType;
        const ext = this.getExtForType(type);
        const filePath = `${p.dir}/${p.fileName}${ext}`;
        let fid = p.fileId || this.client.getFileIdByPath(filePath);
        if (!fid) {
            await this.client.getResourceFiles(p.dir);
            fid = this.client.getFileIdByPath(filePath);
        }
        if (!fid) {
            fid = await this.client.resolveFileIdByPath(filePath);
        }
        if (!fid) {
            throw vscode.FileSystemError.FileNotFound(uri);
        }

        const fileInfo = await this.client.getFile(fid);
        if (!fileInfo) {
            throw vscode.FileSystemError.FileNotFound(uri);
        }

        // datasource 文件以 JSON 形式展示（只读视图）
        if (type === 'datasource') {
            const json = this.serializeDatasourceContent(fileInfo);
            return Buffer.from(json, 'utf8');
        }

        return Buffer.from(fileInfo.script || '', 'utf8');
    }

    // 写入文件
    async writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean; overwrite: boolean; }): Promise<void> {
        if (this.isAgentsUri(uri)) {
            await this.writeAgentsFile(content);
            this._fireSoon({ type: vscode.FileChangeType.Changed, uri });
            return;
        }
        const p = this.parsePath(uri);
        const script = Buffer.from(content).toString('utf8');
        const type = p.dir.split('/')[0] as MagicResourceType;
        const ext = this.getExtForType(type);
        const groupPathSub = p.dir.split('/').slice(1).join('/');

        // 远程工作区：优先解析现有文件ID，避免误走创建
        let fid = p.fileId;
        const fileName = uri.path.split('/').pop()!.replace(new RegExp(`\\${ext}$`), '');
        const filePath = `${p.dir}/${fileName}${ext}`;
        if (!fid) {
            fid = this.client.getFileIdByPath(filePath);
            if (!fid) {
                await this.client.getResourceFiles(p.dir);
                fid = this.client.getFileIdByPath(filePath);
            }
            if (!fid) {
                fid = await this.client.resolveFileIdByPath(filePath);
            }
        }

        try {
            if (fid) {
                const fileInfo = await this.client.getFile(fid);
                if (!fileInfo) {
                    throw vscode.FileSystemError.FileNotFound(uri);
                }

                const ok = await this.client.saveFile({
                    ...fileInfo,
                    script,
                    type
                });
                if (!ok) {
                    const detail = (this.client as any)?.getLastErrorMessage?.();
                    const msg = detail ? `保存失败：服务器未接受更改（详情：${detail}）` : '保存失败：服务器未接受更改';
                    addFileError(uri, msg);
                    throw vscode.FileSystemError.Unavailable(msg);
                }

                clearFileErrors(uri);
                this._fireSoon({ type: vscode.FileChangeType.Changed, uri });
            } else {
                if (!options.create) {
                    throw vscode.FileSystemError.FileNotFound(uri);
                }

                const id = await this.client.createFile({
                    name: fileName,
                    script,
                    type,
                    groupPath: groupPathSub ? `${type}/${groupPathSub}` : `${type}`
                });
                if (!id) {
                    const detail = (this.client as any)?.getLastErrorMessage?.();
                    const msg = detail ? `创建失败：${detail}）` : '创建失败：服务器未返回新文件ID';
                    addFileError(uri, msg);
                    throw vscode.FileSystemError.Unavailable(msg);
                }

                clearFileErrors(uri);
                this._fireSoon({ type: vscode.FileChangeType.Created, uri });
            }
        } catch (e: any) {
            const msg = e?.message ? String(e.message) : String(e);
            addFileError(uri, msg);
            throw e;
        }
    }

    // 删除文件或目录
    async delete(uri: vscode.Uri, options: { recursive: boolean; }): Promise<void> {
        if (this.isAgentsUri(uri)) {
            throw vscode.FileSystemError.NoPermissions('AGENTS.md cannot be deleted');
        }
        const p = this.parsePath(uri);
        if (p.isFile) {
            const type = p.dir.split('/')[0] as MagicResourceType;
            const ext = this.getExtForType(type);
            const filePath = `${p.dir}/${p.fileName}${ext}`;
            let fid = p.fileId || this.client.getFileIdByPath(filePath);
            if (!fid) {
                await this.client.getResourceFiles(p.dir);
                fid = this.client.getFileIdByPath(filePath);
            }
            if (!fid) throw vscode.FileSystemError.FileNotFound(uri);
            await this.client.deleteFile(fid);
        } else {
            const type = p.dir.split('/')[0] as MagicResourceType;
            const groupSub = p.dir.split('/').slice(1).join('/');
            if (!groupSub) {
                throw vscode.FileSystemError.NoPermissions('Cannot delete type root directories');
            }
            await this.client.getResourceDirs();
            const gid = this.client.getGroupIdByPath(`${type}/${groupSub}`);
            if (!gid) throw vscode.FileSystemError.FileNotFound(uri);
            await this.client.deleteGroup(gid);
        }

        this._fireSoon({ type: vscode.FileChangeType.Deleted, uri });
    }

    // 重命名文件或目录
    async rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean; }): Promise<void> {
        if (this.isAgentsUri(oldUri) || this.isAgentsUri(newUri)) {
            throw vscode.FileSystemError.NoPermissions('AGENTS.md cannot be renamed');
        }
        const oldPath = this.parsePath(oldUri);
        const type = oldPath.dir.split('/')[0] as MagicResourceType;
        const ext = this.getExtForType(type);
        const newName = newUri.path.split('/').pop()!.replace(new RegExp(`\\${ext}$`), '');
        const oldGroupPathStr = oldPath.dir.split('/').slice(1).join('/');

        try {
            if (oldPath.fileId) {
                const fileInfo = await this.client.getFile(oldPath.fileId);
                if (!fileInfo) {
                    throw vscode.FileSystemError.FileNotFound(oldUri);
                }

                const ok = await this.client.saveFile({
                    ...fileInfo,
                    name: newName,
                    type
                });
                if (!ok) {
                    const detail = (this.client as any)?.getLastErrorMessage?.();
                    const msg = detail ? `重命名失败：服务器未接受更改（详情：${detail}）` : '重命名失败：服务器未接受更改';
                    addFileError(newUri, msg);
                    throw vscode.FileSystemError.Unavailable(msg);
                }
                clearFileErrors(newUri);
            } else if (!oldPath.isFile) {
                // 目录重命名仍通过分组接口实现
                await this.client.getResourceDirs();
                const gid = this.client.getGroupIdByPath(`${type}/${oldGroupPathStr}`);
                const groupInfo = gid ? await this.client.getGroup(gid) : null;
                if (!groupInfo) {
                    throw vscode.FileSystemError.FileNotFound(oldUri);
                }

                await this.client.saveGroup({
                    ...groupInfo,
                    name: newName
                });
            } else {
                // 尝试解析ID后再重命名
                const fileName = oldUri.path.split('/').pop() || '';
                if (fileName.endsWith(ext)) {
                    const name = fileName.replace(new RegExp(`\\${ext}$`), '');
                    const fileKey = oldGroupPathStr ? `${type}/${oldGroupPathStr}/${name}${ext}` : `${type}/${name}${ext}`;
                    const fid = this.client.getFileIdByPath(fileKey);
                    if (fid) {
                        const fileInfo = await this.client.getFile(fid);
                        if (!fileInfo) throw vscode.FileSystemError.FileNotFound(oldUri);
                        const ok = await this.client.saveFile({ ...fileInfo, name: newName, type });
                        if (!ok) {
                            const detail = (this.client as any)?.getLastErrorMessage?.();
                            const msg = detail ? `重命名失败：服务器未接受更改（详情：${detail}）` : '重命名失败：服务器未接受更改';
                            addFileError(newUri, msg);
                            throw vscode.FileSystemError.Unavailable(msg);
                        }
                        clearFileErrors(newUri);
                    } else {
                        const gid = this.client.getGroupIdByPath(`${type}/${oldGroupPathStr}`);
                        if (gid) {
                            const groupInfo = await this.client.getGroup(gid);
                            if (!groupInfo) throw vscode.FileSystemError.FileNotFound(oldUri);
                            await this.client.saveGroup({ ...groupInfo, name: newName });
                        } else {
                            throw vscode.FileSystemError.FileNotFound(oldUri);
                        }
                    }
                }
            }
        } catch (e: any) {
            const msg = e?.message ? String(e.message) : String(e);
            addFileError(newUri, msg);
            throw e;
        }

        this._fireSoon(
            { type: vscode.FileChangeType.Deleted, uri: oldUri },
            { type: vscode.FileChangeType.Created, uri: newUri }
        );
    }

    // 解析路径
    private parsePath(uri: vscode.Uri): {
        isRoot: boolean;
        isFile: boolean;
        dir: string; // /magic-api/ 后的目录路径，例如 "api/user"
        fileName?: string;
        fileId?: string;
    } {
        const parts = uri.path.split('/').filter(Boolean);
        if (parts.length === 0) {
            return { isRoot: true, isFile: false, dir: '' };
        }
        const type = parts[0] as MagicResourceType;
        const ext = type === 'datasource' ? '.json' : '.ms';
        const last = parts[parts.length - 1] || '';
        const isFile = last.endsWith(ext);
        const dir = isFile ? parts.slice(0, -1).join('/') : parts.join('/');
        const fileName = isFile ? last.replace(new RegExp(`\\${ext}$`), '') : undefined;
        let fileId: string | undefined;
        if (isFile) {
            const key = `${dir}/${fileName}${ext}`;
            fileId = this.client.getFileIdByPath(key);
        }
        return { isRoot: false, isFile, dir, fileName, fileId };
    }

    // 将数据源信息序列化为只读 JSON 内容
    private serializeDatasourceContent(fileInfo: MagicFileInfo): string {
        // 优先展示服务端的完整原始对象
        if (fileInfo && (fileInfo as any).raw) {
            try {
                return JSON.stringify((fileInfo as any).raw, null, 2);
            } catch(e) {console.warn(e)}
        }
        // 兜底：以现有字段近似构造
        const fallback: any = {
            properties: fileInfo.extra || {},
            id: fileInfo.id,
            script: fileInfo.script ?? null,
            groupId: fileInfo.groupId,
            name: fileInfo.name,
            createTime: fileInfo.createTime,
            updateTime: fileInfo.updateTime,
            lock: (fileInfo as any).lock ?? undefined,
            createBy: fileInfo.createBy,
            updateBy: fileInfo.updateBy
        };
        return JSON.stringify(fallback, null, 2);
    }

    // 触发文件变化事件
    private _fireSoon(...events: vscode.FileChangeEvent[]): void {
        this._bufferedEvents.push(...events);

        if (this._fireSoonHandle) {
            clearTimeout(this._fireSoonHandle);
        }

        this._fireSoonHandle = setTimeout(() => {
            this._emitter.fire(this._bufferedEvents);
            this._bufferedEvents.length = 0;
        }, 5);
    }
}